package simple

import "sync"

// Cache хранилище данных в памяти.
type Cache[K comparable, V any] struct {
	mutex sync.RWMutex
	items map[K]V
}

// NewCache инициализирует новый кеш в памяти.
func NewCache[K comparable, V any]() *Cache[K, V] {
	return &Cache[K, V]{
		items: make(map[K]V),
	}
}

// Set устанавливает значение.
func (c *Cache[K, V]) Set(key K, value V) {
	c.mutex.Lock()
	defer c.mutex.Unlock()
	c.items[key] = value
}

// Get получает значение.
func (c *Cache[K, V]) Get(key K) (V, bool) {
	c.mutex.RLock()
	defer c.mutex.RUnlock()
	item, found := c.items[key]

	return item, found
}

// Delete удаляет значение.
func (c *Cache[K, V]) Delete(key K) {
	c.mutex.Lock()
	defer c.mutex.Unlock()
	delete(c.items, key)
}

// GetAndDelete получает значение и удаляет его из списка.
func (c *Cache[K, V]) GetAndDelete(key K) (V, bool) {
	c.mutex.Lock()
	item, found := c.items[key]
	delete(c.items, key)
	c.mutex.Unlock()

	return item, found
}

// Exists проверяет, существует ли ключ.
func (c *Cache[K, V]) Exists(key K) bool {
	c.mutex.RLock()
	defer c.mutex.RUnlock()
	_, found := c.items[key]

	return found
}

// Clear очищает кеш.
func (c *Cache[K, V]) Clear() {
	c.mutex.Lock()
	defer c.mutex.Unlock()
	clear(c.items)
}

// Append добавляет карту к кешу.
func (c *Cache[K, V]) Append(list map[K]V) int {
	c.mutex.Lock()
	defer c.mutex.Unlock()

	for key, val := range list {
		c.items[key] = val
	}

	return len(c.items)
}

// Count количество данных в карте.
func (c *Cache[K, V]) Count() int {
	c.mutex.RLock()
	defer c.mutex.RUnlock()

	return len(c.items)
}

// Values возвращает значения в виде массива.
func (c *Cache[K, V]) Values() []V {
	c.mutex.RLock()
	defer c.mutex.RUnlock()

	result := make([]V, 0, len(c.items))
	for _, val := range c.items {
		result = append(result, val)
	}

	return result
}

// Keys возвращает значения в виде массива.
func (c *Cache[K, V]) Keys() []K {
	c.mutex.RLock()
	defer c.mutex.RUnlock()

	result := make([]K, 0, len(c.items))
	for key := range c.items {
		result = append(result, key)
	}

	return result
}
